#ifndef __EPSILON__WINDOW_H__
#define __EPSILON__WINDOW_H__

#include "debug.h"
//#include "object.h"
#include "types.h"

#include "tchar_epsilon.h"

typedef TCHAR eWindowString [256];

class eWindow;

namespace Epsilon
{
  class Keyboard;
  class Mouse;
}

/**
 * A primitive window handle object.
 *
 */
struct eWindowHandle
{
friend class eWindow;
friend class eWindowWin32;
friend class eWindowX11;
friend class eWindowQuartz;
protected:
  eWindowHandle (DWORD dwHandle, eWindow* pWindow) : handle (dwHandle),
                                                     window (pWindow) { };
public:
  operator const DWORD&   (void) { return handle; };
  operator       eWindow* (void) { return window; };

private:
  DWORD    handle;
  eWindow* window;
};

/**
 * This struct is passed to eWindow for creation purposes.
 *
 */
class eWindowParameters {
public:
  short         size;            ///< Size of this structure.    Used internally to preserve compatability with older applications
//  short         version;         ///< Version of this structure. Used internally to preserve compatability with older applications
  int           flags;           ///< Flags.                     ** Reserved for future use

  eWindowString window_name;     ///< Desired window name.

  short         window_width;    ///< Requested window width
  short         window_height;   ///< Requested window height

  /// Default constructor
  EPSILON_API eWindowParameters (void);

  enum {
    E_WF_NONE   = 0x00,

    E_WF_WIN32  = 0x01,
    E_WF_X11    = 0x02,
    E_WF_QUARTZ = 0x04,
  };
};


/**
 * This struct is used to notify the host application when
 *  the dimensions/position of our eWindow have changed...
 *
 */
struct eWindowRect {
  eRect rcWindow;         ///< The entire window's dimensions (includes title bar...
  eRect rcClient;         ///< Client (viewport)'s dimensions

  UINT  cxWindowBorders;  ///< Size of the window's border in terms of X
  UINT  cyWindowBorders;  ///< Size of the window's border in terms of Y
};


class eWindow;

typedef bool (CALLBACK * PFN_eWindowResizeCallback)(eWindow* window, eWindowRect& rect); /// Window resize callback (C-Style)
typedef bool (CALLBACK * PFN_eWindowMoveCallback)(eWindow* window,   eWindowRect& rect); /// Window move callback   (C-Style)

/**
 * This interface is inherited by classes that require notification
 *  upon special window events.
 *
 */
class e_iWindowListener {
friend class eWindow;
// TODO: Use eWindowInternals for listener notifications...
#if 0
friend class eWindowInternals;
#else
friend class eWindowWin32;
friend class eWindowX11;
friend class eWindowQuartz;
#endif 
protected:
  virtual bool OnMove (eWindowRect& rect) {
    return true;
  };

  virtual bool OnResize (eWindowRect& rect) {
    return true;
  };
};



class eWindowInternals {
protected:
           eWindowInternals (void);
  virtual ~eWindowInternals (void);

  virtual bool create   (const eWindowParameters& parameters, eWindow* window) = 0; ///< Create the window and perform default initialization
  virtual bool recreate (const eWindowParameters& parameters) = 0;                  ///< Recreate the window (that is, change render context properties on the fly...)
  virtual bool destroy  (void) = 0;                                                 ///< Destroy the window and perform all cleanup

  virtual void move          (const int& left, const int& top) = 0; ///< Move the window to the specified left and top coordinates
  virtual void move_client   (const int& left, const int& top) = 0; ///< Move the client region of the window
  virtual void resize        (const int& x,    const int& y)   = 0; ///< Resize the window to the specified x and y dimensions
  virtual void resize_client (const int& x,    const int& y)   = 0; ///< Resize the client region of the window

  virtual void show             (int cmd)      = 0; ///< Allows you to show or hide the window.

  virtual void show_cursor      (bool show)    = 0; ///< Show or Hide the Window Manager's cursor.
  virtual void set_cursor_pos   (int x, int y) = 0; ///< From client coords, set the Window Manager's logical cursor position.

  //// Is the WM (Hardware) cursor visible?
  inline bool get_cursor_state (void) {
    return cursorShown;
  };

  virtual void update_rect (void) = 0;                        ///< Causes the engine to requery the dimensions from the Window Manager...

  virtual
    const eWindowHandle
              getNativeWindowHandle (void) = 0;               ///< Returns an API specific window handle.

  void        activate   (void) { wndActive = true;     }; ///< Notifies the window that it received foreground window status
  void        deactivate (void) { wndActive = false;    }; ///< Notifies the window that it lost foreground window status
  const bool& active     (void) { return wndActive;     }; ///< Indicates whether or not the window is "active"

  inline const eWindowRect& rect (void) { return wndRect; }; ///< Returns the window's rect...

  inline int width  (void) { return wndRect.rcClient.right -
                                    wndRect.rcClient.left;   }; ///< Returns the window's width
  inline int height (void) { return wndRect.rcClient.bottom - 
                                    wndRect.rcClient.top;    }; ///< Returns the window's height

  


  bool          initialized; ///< Is the window initialized?

  bool          wndActive;   ///< Is this window active (foreground)?
  bool          wndShown;    ///< Is this window visible?
  bool          cursorShown; ///< Stores the visibility of the WM cursor.

  eWindowRect   wndRect;     ///< Stores the window's rect...

  Epsilon::Keyboard*
                keyboard;    ///< Non-NULL if this window has its own input subsystem.

  Epsilon::Mouse*
                mouse;       ///< Non-NULL if this window has its own input subsystem.

  eWindow*      window;      ///< Stores a pointer to the general window object.
};



#ifdef WIN32
#  include "platform/Win32/window_win32.h"
#else
#  ifndef __APPLE__
#    include "platform/Linux/window_x11.h"
#  else
#    include "platform/MacOSX/window_quartz.h"
#  endif
#endif



/**
 * This is our window class for Epsilon...
 *
 * An eWindow serves as both, a standalone and
 *  interface class for other window classes.
 *
 */
class eWindow /*: public Epsilon::Object*/ {
public:
                       eWindow (void);
  EPSILON_API virtual ~eWindow (void);

  EPSILON_API bool Create   (const eWindowParameters& parameters);  ///< Create the window and perform default initialization
  EPSILON_API bool Recreate (const eWindowParameters& parameters);  ///< Recreate the window (that is, change render context properties on the fly...)
  EPSILON_API bool Destroy  (void);                                 ///< Destroy the window and perform all cleanup

  EPSILON_API void Move         (const int& left, const int& top);  ///< Move the window to the specified left and top coordinates
  EPSILON_API void MoveClient   (const int& left, const int& top);  ///< Move the client region to the specified left and top coordinates
  EPSILON_API void Resize       (const int& x,    const int& y);    ///< Resize the window to the specified x and y dimensions
  EPSILON_API void ResizeClient (const int& x,    const int& y);    ///< Resize the client region to the specified x and y dimensions

  EPSILON_API void Show (int cmd);                         ///< Allows you to show or hide the window.

  void        Activate     (void) { internals.activate      (); }; ///< Notifies the window that it received foreground window status
  void        Deactivate   (void) { internals.deactivate    (); }; ///< Notifies the window that it lost foreground window status
  const bool& isActive     (void) { return internals.active (); }; ///< Indicates whether or not the window is "active"

  /// Show or Hide the cursor (when it is within this window's rect...)
  void ShowCursor     (bool show) { internals.show_cursor (show);         };

  /// Is the cursor visible?
  ///
  ///   >> TODO: Include relative window position, in addition to show / hide
  ///              state...
  bool GetCursorState (void)      { return internals.get_cursor_state (); };

  /// Set the Window Manager's logical (relative to desktop resolution) cursor
  ///   position, given a point relative to this window's top-left corner
  ///     (client coordinates).
  void SetCursorPos (int x, int y) { internals.set_cursor_pos (x, y); };

  /// General WriteScreenshot method.
  ///  * Automatically calls CaptureScreen (...) for you.
  EPSILON_API bool writeScreenshot  (void);

  /// Advanced WriteScreenshot overload.
  ///  * Write a new screenshot using the passed pixel bitmap (imageData) at a resolution of
  ///    (resX x resY pixels) at a depth of (bpp bits per pixel).
  EPSILON_API bool writeScreenshot (uchar* imageData, const int& resX, const int& resY, const int& bpp);

  ///
  /// Copies the contents of the BACK buffer to the supplied uchar*...
  ///
  ///  *** (Allocates pOutBuffer for you!) ***
  ///        @ You must free it using
  ///          e_free if you don't want
  ///          to cause a crash...
  ///
  EPSILON_API void captureScreen   (uchar** pOutBuffer, int& bpp);

  inline const eWindowRect& getRect (void) { return internals.rect (); }; ///< Returns the window's rect...

  inline int getWidth  (void) { return internals.width  (); }; ///< Returns the window's width
  inline int getHeight (void) { return internals.height (); }; ///< Returns the window's height

  inline void installListener (e_iWindowListener* listener_) {
    Assert (listener_ != NULL, _T ("Attempting to install an invalid window listener..."));
    wndListener = listener_;
  }

  inline void setResizeCallback (const PFN_eWindowResizeCallback callback_) {
    Assert (callback_ != NULL, _T ("Attempting to install an invalid resize callback..."));
    wndResizeCallback = callback_;
  }

  inline void setMoveCallback (const PFN_eWindowMoveCallback callback_) {
    Assert (callback_ != NULL, _T ("Attempting to install an invalid move callback..."));
    wndMoveCallback = callback_;
  }

  inline Epsilon::Keyboard* getKeyboard (void) { return internals.keyboard; }
  inline Epsilon::Mouse*    getMouse    (void) { return internals.mouse;    }

  inline const eWindowHandle getHandle (void) {
    return eWindowHandle (0, this);
  }

  ///< Returns an API specific window handle.
  inline const eWindowHandle getNativeWindowHandle (void) {
    return internals.getNativeWindowHandle ();
  }

  bool          fullscreen; ///< Is this window fullscreen?
  eWindowString title;      ///< This window's name

private:
  e_iWindowListener*        wndListener;       ///< Stores the installed window listener (ONLY supports 1 per-window right now...)
  PFN_eWindowResizeCallback wndResizeCallback; ///< Installed resize callback
  PFN_eWindowMoveCallback   wndMoveCallback;   ///< Installed move callback

#ifdef WIN32
  friend class  eWindowWin32;
  eWindowWin32  internals;
#else
#ifndef __APPLE__
  friend class  eWindowX11;
  eWindowX11    internals;
#else
  friend class  eWindowQuartz;
  eWindowQuartz internals;
#endif
#endif
};


#endif /* __EPSILON__WINDOW_H__ */
